%{ open Ast %}

//      (       )      {     }        [       ]     ;      ,
%token LPAREN RPAREN LBRACE RBRACE LSBRACE RSBRACE SEMI COMMA 
//        ++         --
%token PLUSPLUS MINUSMINUS
//     == != <  <= >  >=
%token EQ NE LT LE GT GE
//      && ||  !  .
%token AND OR NOT DOT
//      +     -     *      /    %
%token PLUS MINUS TIMES DIVIDE MOD
//       =      +=              -=          *=         %=                   
%token ASSIGN PLUS_ASSIGN MINUS_ASSIGN TIMES_ASSIGN DIVIDE_ASSIGN MOD_ASSIGN
//reserved keywords for control flow
%token RETURN IF ELSE FOR BREAK
//reserved keywords for types
%token INT DOUBLE STRING LIST POINT SHAPE IMAGE STRUCT
%token <string> ID
//constants
%token <int> CONST_INT
%token <float> CONST_DBL
%token <string> CONST_STR

%token EOF

%nonassoc DECL
%nonassoc NOELSE
%nonassoc IF ELSE RETURN FOR BREAK

%right ASSIGN PLUS_ASSIGN MINUS_ASSIGN TIMES_ASSIGN DIVIDE_ASSIGN MOD_ASSIGN COMMA
%left OR
%left AND
%left EQ NE
%left LT GT LE GE
%left PLUS MINUS
%left TIMES DIVIDE MOD
%right NOT
%left PLUSPLUS MINUSMINUS DOT LPAREN RPAREN LSBRACE RSBRACE LABRACE RABRACE

%start program
%type <Ast.program> program

%%

program: 
	  function_definition_list					{ Print_prog(None, $1) }
	| declaration_list function_definition_list			{ Print_prog(Some($1), $2) }

expr:
	  ID LPAREN expr_list_opt RPAREN				{ Print_expr_func($1, $3) }
	| constant							{ Print_expr_const($1) }
	| lvalue							{ Print_expr_lval($1, None) }  
	| MINUS expr %prec NOT						{ Print_expr_op("-", $2) }
	| NOT expr							{ Print_expr_op("!", $2) }
	| lvalue PLUSPLUS						{ Print_expr_lval($1, Some("++")) }
	| lvalue MINUSMINUS						{ Print_expr_lval($1, Some("--")) }
	| expr binop expr %prec OR					{ Print_expr_binop($1, $2, $3) }
	| lvalue asgnop expr %prec ASSIGN				{ Print_expr_asgop($1, $2, $3) }
	| LPAREN expr RPAREN  	    					{ Print_expr_paren($2) }

expr_list_opt:
                                                                        { Print_expr_list_opt(None) }
        | expr_list     						{ Print_expr_list_opt(Some($1)) }

expr_list:
          expr								{ Print_expr_list(None, $1) }
        | expr_list COMMA expr          				{ Print_expr_list(Some($1), $3) }

lvalue:
	  ID								{ Print_lval_id($1) }
	| lvalue LSBRACE expr RSBRACE					{ Print_lval_arr($1, $3) }
	| lvalue DOT ID	       	    					{ Print_lval_obj($1, $3) }

binop:
	  TIMES								{ "*" }
	| DIVIDE							{ "/" }
	| MOD								{ "%" }
	| PLUS								{ "+" }
	| MINUS								{ "-" }
	| LT								{ "<" }
	| GT								{ ">" }
	| LE								{ "<=" }
	| GE								{ ">=" }
	| EQ								{ "==" }
	| NE								{ "!=" }
	| AND								{ "&&" }
	| OR								{ "||" }

asgnop:
	  ASSIGN							{ "=" }
	| PLUS_ASSIGN							{ "+=" }
	| MINUS_ASSIGN							{ "-=" }
	| TIMES_ASSIGN							{ "*=" }
	| DIVIDE_ASSIGN							{ "/=" }
	| MOD_ASSIGN							{ "%=" }

stmt_list:
	  stmt  	      						{ Print_stmt_list(None, $1) }
	| stmt_list stmt						{ Print_stmt_list(Some($1), $2) }

stmt:
   	  expr SEMI							{ Print_stmt($1) }
	| LBRACE stmt_list RBRACE					{ Print_stmt_block($2) }
	| IF LPAREN expr RPAREN stmt %prec NOELSE			{ Print_stmt_if($3, $5) }
	| IF LPAREN expr RPAREN stmt ELSE stmt		  	     	{ Print_stmt_ifelse($3, $5, $7) }
	| FOR LPAREN expr_opt SEMI expr_opt SEMI expr_opt RPAREN stmt	{ Print_stmt_for($3, $5, $7, $9) }
	| BREAK SEMI							{ Print_stmt_break }
	| RETURN expr SEMI						{ Print_stmt_ret($2) }
	| SEMI	      							{ Print_stmt_noop }

expr_opt:
	  /* nothing */							{ Print_expropt(None) }
	| expr 	   							{ Print_expropt(Some($1)) }
	
declaration_list:
	  declaration							{ Print_decl_list(None, $1) }
	| declaration_list declaration					{ Print_decl_list(Some($1), $2) }

declaration:
	  type_specifier declarator_list SEMI				{ Print_decl($1, $2) }
        | STRUCT ID LBRACE declaration_list RBRACE			{ Print_decl_struct($2, $4) }

type_specifier:
	  INT								{ Print_type_str("int") }
	| DOUBLE							{ Print_type_str("double") }
	| STRING							{ Print_type_str("string") }
	| LIST LT type_specifier GT %prec DOT				{ Print_type_list($3) }
	| POINT								{ Print_type_str("point") }
	| SHAPE								{ Print_type_str("shape") }
	| IMAGE								{ Print_type_str("image") }
	| STRUCT ID  	   		    				{ Print_type_struct($2) }
		
declarator_list:
	  declarator							{ Print_declarator_list(None, $1) }
	| declarator_list COMMA declarator				{ Print_declarator_list(Some($1), $3) }

declarator:
	  ID								{ Print_declarator($1, None) }
	| ID initializer_						{ Print_declarator($1, Some($2)) }

initializer_:
	  ASSIGN constant_expr						{ Print_init_const($2) }
	| ASSIGN LBRACE constant_expr_list RBRACE			{ Print_init_const_list($3) }

constant_expr:
	  constant							{ Print_const_expr($1) }
	| MINUS constant_expr %prec NOT					{ Print_const_expr_neg($2) }
	| NOT constant_expr						{ Print_const_expr_not($2) }
	| constant_expr binop constant_expr %prec OR			{ Print_const_expr_binop($1, $2, $3) }
	| LPAREN constant_expr RPAREN					{ Print_const_expr_paren($2) }

constant:
	  CONST_INT							{ Print_const_int($1) }
	| CONST_DBL							{ Print_const_dbl($1) }
	| CONST_STR							{ Print_const_str($1) }

constant_expr_list:
	  constant_expr							{ Print_const_expr_list(None, $1) }
	| constant_expr_list COMMA constant_expr			{ Print_const_expr_list(Some($1), $3) }

function_definition:
	  type_specifier function_declarator function_body		{ Print_func_def($1, $2, $3) }

function_definition_list:
	  function_definition						{ Print_func_def_list(None, $1) }
        | function_definition_list function_definition			{ Print_func_def_list(Some($1), $2) }

function_declarator:
	  ID LPAREN param_list_opt RPAREN				{ Print_func_decl($1, $3) }

param_list_opt:
	  /* nothing */							{ Print_param_list_opt(None) }
	| parameter_list						{ Print_param_list_opt(Some($1)) }

parameter_list:
	  function_parameter						{ Print_param_list(None, $1) }
	| parameter_list COMMA function_parameter			{ Print_param_list(Some($1), $3) }

function_parameter:
	  type_specifier ID						{ Print_func_param($1, $2) }

function_body:
	  LBRACE declaration_list_opt stmt_list RBRACE			{ Print_funcbody($2, $3) }

declaration_list_opt:		      					
	  /* nothing */							{ Print_decl_list_opt(None) }
	| declaration_list						{ Print_decl_list_opt(Some($1)) }